using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;

namespace HslControls.GameSupport
{
	/// <summary>
	/// 游戏控件中活动状态的游戏单元
	/// </summary>
	public class MoveGameItem
	{
		private Point[] activePoints;

		private Size boardSize;

		private Func<Point[], bool> moveJudge = null;

		private int direction = 0;

		private int rank = 3;

		private static Random random = new Random();

		/// <summary>
		/// 绘图的颜色
		/// </summary>
		public Color PaintColor
		{
			get;
			set;
		} = Color.Black;


		/// <summary>
		/// 默认的位置
		/// </summary>
		public Point LocationDefault
		{
			get;
			set;
		}

		/// <summary>
		/// 实际的位置
		/// </summary>
		public Point Location
		{
			get;
			set;
		}

		/// <summary>
		/// 获取方向
		/// </summary>
		public int Direction
		{
			get
			{
				return direction;
			}
			set
			{
				direction = value;
			}
		}

		/// <summary>
		/// 实例化一个游戏的活动单元对象，默认每个活动单元都是4*4的一个大小方块
		/// </summary>
		/// <param name="pt">该对象包含的所有的点位信息</param>
		/// <param name="locationDefault">该活动单元所处的游戏界面的默认位置</param>
		/// <param name="board">实际游戏画板的大小</param>
		/// <param name="moveJudge">是否允许变换的判断，主要用于碰撞检测</param>
		public MoveGameItem(Point[] pt, Point locationDefault, Size board, Func<Point[], bool> moveJudge)
		{
			activePoints = pt;
			boardSize = board;
			LocationDefault = locationDefault;
			Location = locationDefault;
			this.moveJudge = moveJudge;
			CalculatePointsRank();
		}

		/// <summary>
		/// 根据启动的活动单元初始化游戏的位置
		/// </summary>
		/// <param name="tp">游戏的活动单元</param>
		public MoveGameItem(MoveGameItem tp)
		{
			boardSize = default(Size);
			Location = LocationDefault;
			SetNewPoints(tp.GetItemPoints());
			random = new Random();
		}

		/// <summary>
		/// 获取所有真实的位置数据信息，经过转换到实际的游戏坐标上的
		/// </summary>
		public Point[] GetActualPoints()
		{
			Point[] points = new Point[activePoints.Length];
			for (int i = 0; i < points.Length; i++)
			{
				points[i] = new Point(activePoints[i].X + Location.X, activePoints[i].Y + Location.Y);
			}
			return points;
		}

		/// <summary>
		/// 获取移动单元的点位数据，这些数据的相对的坐标，在解析界面和实际的相对坐标组合成实际的坐标
		/// </summary>
		/// <returns>数据点位的信息</returns>
		public Point[] GetItemPoints()
		{
			return activePoints.ToArray();
		}

		/// <summary>
		/// 设置新的图形及位置信息
		/// </summary>
		/// <param name="points">图形信息</param>
		public void SetNewPoints(Point[] points)
		{
			Location = LocationDefault;
			activePoints = new Point[points.Length];
			for (int i = 0; i < activePoints.Length; i++)
			{
				activePoints[i] = new Point(points[i].X, points[i].Y);
			}
			direction = 0;
			CalculatePointsRank();
		}

		/// <summary>
		/// 随机的移动方向
		/// </summary>
		/// <returns>是否随机</returns>
		public bool MoveRandom()
		{
			if (random.Next(100) < 80)
			{
				return MoveNext();
			}
			List<int> directions = new List<int>
			{
				0,
				1,
				2,
				3
			};
			while (directions.Count > 0)
			{
				int index = random.Next(directions.Count);
				if (TurnDirection(directions[index]))
				{
					return true;
				}
				directions.RemoveAt(index);
			}
			return false;
		}

		/// <summary>
		/// 向左移动，前提是能移动
		/// </summary>
		/// <returns>是否成功的移动</returns>
		public bool MoveLeft()
		{
			bool result = false;
			if (moveJudge(GetLeftPoints()))
			{
				Location = new Point(Location.X - 1, Location.Y);
				result = true;
			}
			return result;
		}

		/// <summary>
		/// 旋转方向为左，前提是能旋转
		/// </summary>
		/// <returns>是否发生了方向变化</returns>
		public bool TurnLeft()
		{
			bool result = false;
			if (direction != 3 && moveJudge(GetTurnLeftPoints()))
			{
				activePoints = RotationChangePoints(3 - direction);
				while (RightXPoint(GetActualPoints()))
				{
					Location = new Point(Location.X - 1, Location.Y);
				}
				direction = 3;
				result = true;
			}
			return result;
		}

		/// <summary>
		/// 向右移动，前提是能移动
		/// </summary>
		/// <returns>是否成功的移动</returns>
		public bool MoveRight()
		{
			bool result = false;
			if (moveJudge(GetRightPoints()))
			{
				Location = new Point(Location.X + 1, Location.Y);
				result = true;
			}
			return result;
		}

		/// <summary>
		/// 旋转方向为右，前提是能旋转
		/// </summary>
		/// <returns>是否发生了方向变化</returns>
		public bool TurnRight()
		{
			bool result = false;
			if (direction != 1 && moveJudge(GetTurnRightPoints()))
			{
				activePoints = RotationChangePoints((direction > 1) ? (5 - direction) : (1 - direction));
				while (RightXPoint(GetActualPoints()))
				{
					Location = new Point(Location.X - 1, Location.Y);
				}
				direction = 1;
				result = true;
			}
			return result;
		}

		/// <summary>
		/// 向上移动，前提是能移动
		/// </summary>
		/// <returns>是否成功的移动</returns>
		public bool MoveUp()
		{
			bool result = false;
			if (moveJudge(GetUpPoints()))
			{
				Location = new Point(Location.X, Location.Y + 1);
				result = true;
			}
			return result;
		}

		/// <summary>
		/// 旋转方向为上，前提是能旋转
		/// </summary>
		/// <returns>是否发生了方向变化</returns>
		public bool TurnUp()
		{
			bool result = false;
			if (direction != 0 && moveJudge(GetTurnUpPoints()))
			{
				activePoints = RotationChangePoints(4 - direction);
				while (RightXPoint(GetActualPoints()))
				{
					Location = new Point(Location.X - 1, Location.Y);
				}
				direction = 0;
				result = true;
			}
			return result;
		}

		/// <summary>
		/// 向下移动，前提是能移动
		/// </summary>
		/// <returns>是否成功的移动</returns>
		public bool MoveDown()
		{
			bool result = false;
			if (moveJudge(GetDownPoints()))
			{
				Location = new Point(Location.X, Location.Y - 1);
				result = true;
			}
			return result;
		}

		/// <summary>
		/// 旋转方向为下，前提是能旋转
		/// </summary>
		/// <returns>是否发生了方向变化</returns>
		public bool TurnDown()
		{
			bool result = false;
			if (direction != 2 && moveJudge(GetTurnDownPoints()))
			{
				activePoints = RotationChangePoints((direction > 2) ? (6 - direction) : (2 - direction));
				while (RightXPoint(GetActualPoints()))
				{
					Location = new Point(Location.X - 1, Location.Y);
				}
				direction = 2;
				result = true;
			}
			return result;
		}

		/// <summary>
		/// 根据当前的方向，活动单元继续移动
		/// </summary>
		/// <returns>是否发生了移动</returns>
		public bool MoveNext()
		{
			return direction switch
			{
				0 => MoveUp(), 
				1 => MoveRight(), 
				2 => MoveDown(), 
				3 => MoveLeft(), 
				_ => false, 
			};
		}

		/// <summary>
		/// 根据指定的方向转动活动单元
		/// </summary>
		/// <param name="direction">方向，0,1,2,3</param>
		/// <returns>是否转动了方向</returns>
		public bool TurnDirection(int direction)
		{
			return direction switch
			{
				0 => TurnUp(), 
				1 => TurnRight(), 
				2 => TurnDown(), 
				3 => TurnLeft(), 
				_ => false, 
			};
		}

		/// <summary>
		/// 旋转变化，前提是能旋转，顺时针旋转90度
		/// </summary>
		/// <returns>是否成功的转动</returns>
		public bool RotationChange()
		{
			bool result = false;
			if (moveJudge(GetRotationPoints(1)))
			{
				activePoints = RotationChangePoints(1);
				while (RightXPoint(GetActualPoints()))
				{
					Location = new Point(Location.X - 1, Location.Y);
				}
				direction++;
				if (direction >= 4)
				{
					direction = 0;
				}
				result = true;
			}
			return result;
		}

		/// <summary>
		/// 获取向上移动后的在游戏界面的实际坐标位置，主要用于碰撞检测的
		/// </summary>
		/// <returns>转换后的实际坐标</returns>
		public Point[] GetUpPoints()
		{
			Point[] ptNow = new Point[activePoints.Length];
			for (int i = 0; i < activePoints.Length; i++)
			{
				ptNow[i] = new Point(activePoints[i].X + Location.X, activePoints[i].Y + Location.Y + 1);
			}
			return ptNow;
		}

		/// <summary>
		/// 获取向下移动后的在游戏界面的实际坐标位置，主要用于碰撞检测的
		/// </summary>
		/// <returns>转换后的实际坐标</returns>
		public Point[] GetDownPoints()
		{
			Point[] ptNow = new Point[activePoints.Length];
			for (int i = 0; i < activePoints.Length; i++)
			{
				ptNow[i] = new Point(activePoints[i].X + Location.X, activePoints[i].Y + Location.Y - 1);
			}
			return ptNow;
		}

		/// <summary>
		/// 获取向左移动后的在游戏界面的实际坐标位置，主要用于碰撞检测的
		/// </summary>
		/// <returns>转换后的实际坐标</returns>
		public Point[] GetLeftPoints()
		{
			Point[] ptNow = new Point[activePoints.Length];
			for (int i = 0; i < activePoints.Length; i++)
			{
				ptNow[i] = new Point(activePoints[i].X + Location.X - 1, activePoints[i].Y + Location.Y);
			}
			return ptNow;
		}

		/// <summary>
		/// 获取向右移动后的在游戏界面的实际坐标位置，主要用于碰撞检测的
		/// </summary>
		/// <returns>转换后的实际坐标</returns>
		public Point[] GetRightPoints()
		{
			Point[] ptNow = new Point[activePoints.Length];
			for (int i = 0; i < activePoints.Length; i++)
			{
				ptNow[i] = new Point(activePoints[i].X + Location.X + 1, activePoints[i].Y + Location.Y);
			}
			return ptNow;
		}

		/// <summary>
		/// 获取顺时针旋转后的在游戏界面的实际坐标位置，主要用于碰撞检测的
		/// </summary>
		/// <returns>转换后的实际坐标</returns>
		public Point[] GetRotationPoints(int rate)
		{
			Point[] ptNow = RotationChangePoints(rate);
			for (int i = 0; i < activePoints.Length; i++)
			{
				ptNow[i] = new Point(ptNow[i].X + Location.X, ptNow[i].Y + Location.Y);
			}
			while (RightXPoint(ptNow))
			{
				for (int j = 0; j < ptNow.Length; j++)
				{
					ptNow[j].X--;
				}
			}
			return ptNow;
		}

		/// <summary>
		/// 获取活动单元方向朝上的实际坐标位置，主要用于碰撞检测的
		/// </summary>
		/// <returns>转换后的实际坐标</returns>
		public Point[] GetTurnUpPoints()
		{
			int rate = (direction > 0) ? (4 - direction) : (-direction);
			return GetRotationPoints(rate);
		}

		/// <summary>
		/// 获取活动单元方向朝右的实际坐标位置，主要用于碰撞检测的
		/// </summary>
		/// <returns>转换后的实际坐标</returns>
		public Point[] GetTurnRightPoints()
		{
			int rate = (direction > 1) ? (5 - direction) : (1 - direction);
			return GetRotationPoints(rate);
		}

		/// <summary>
		/// 获取活动单元方向朝下的实际坐标位置，主要用于碰撞检测的
		/// </summary>
		/// <returns>转换后的实际坐标</returns>
		public Point[] GetTurnDownPoints()
		{
			int rate = (direction > 2) ? (6 - direction) : (2 - direction);
			return GetRotationPoints(rate);
		}

		/// <summary>
		/// 获取活动单元方向朝左的实际坐标位置，主要用于碰撞检测的
		/// </summary>
		/// <returns>转换后的实际坐标</returns>
		public Point[] GetTurnLeftPoints()
		{
			return GetRotationPoints(3 - direction);
		}

		/// <summary>
		/// 判断指定的位置是否有数据
		/// </summary>
		/// <param name="point">位置信息</param>
		/// <returns>是否存在数据信息</returns>
		public bool IsPositionInMoveItem(Point point)
		{
			for (int i = 0; i < activePoints.Length; i++)
			{
				if (point.X == activePoints[i].X + Location.X && point.Y == activePoints[i].Y + Location.Y)
				{
					return true;
				}
			}
			return false;
		}

		/// <summary>
		/// 判断点数的位置是否在活动对象中
		/// </summary>
		/// <param name="points">点位数据</param>
		/// <returns>是否存在数据信息</returns>
		public bool IsPositionInMoveItem(Point[] points)
		{
			for (int i = 0; i < points.Length; i++)
			{
				if (IsPositionInMoveItem(points[i]))
				{
					return true;
				}
			}
			return false;
		}

		/// <summary>
		/// 判断整个点位信息是否从0列开始，这个是基于相对坐标而言的
		/// </summary>
		/// <param name="points">点数数据</param>
		/// <returns>成功与否</returns>
		private bool ResetXPoint(Point[] points)
		{
			for (int i = 0; i < points.Length; i++)
			{
				if (points[i].X == 0)
				{
					return false;
				}
			}
			return true;
		}

		/// <summary>
		/// 判断整个点位信息是否从0行开始，这个是基于相对坐标而言的
		/// </summary>
		/// <param name="points">点位信息</param>
		/// <returns>是否成功</returns>
		private bool ResetYPoint(Point[] points)
		{
			for (int i = 0; i < points.Length; i++)
			{
				if (points[i].Y == 0)
				{
					return false;
				}
			}
			return true;
		}

		/// <summary>
		/// 判断是否超出了右边界，这个是基于相对坐标而言的
		/// </summary>
		/// <param name="points">点位信息</param>
		/// <returns>是否成功</returns>
		private bool RightXPoint(Point[] points)
		{
			for (int i = 0; i < points.Length; i++)
			{
				if (points[i].X >= boardSize.Width)
				{
					return true;
				}
			}
			return false;
		}

		/// <summary>
		/// 获取旋转及位置变化后的点位数据，旋转指定的角度倍数
		/// </summary>
		/// <param name="rate">倍率关系，1表示向右，</param>
		/// <returns>数据信息</returns>
		private Point[] RotationChangePoints(int rate)
		{
			Point[] points = new Point[activePoints.Length];
			for (int j = 0; j < activePoints.Length; j++)
			{
				points[j] = new Point(activePoints[j].X, activePoints[j].Y);
			}
			for (int m = 0; m < rate; m++)
			{
				Point[] ptNow = new Point[points.Length];
				for (int l = 0; l < points.Length; l++)
				{
					ptNow[l] = new Point(points[l].Y, rank - points[l].X);
				}
				while (ResetXPoint(ptNow))
				{
					for (int i = 0; i < ptNow.Length; i++)
					{
						ptNow[i].X--;
					}
				}
				while (ResetYPoint(ptNow))
				{
					for (int k = 0; k < ptNow.Length; k++)
					{
						ptNow[k].Y--;
					}
				}
				points = ptNow;
			}
			return points;
		}

		private void CalculatePointsRank()
		{
			rank = 0;
			for (int i = 0; i < activePoints.Length; i++)
			{
				if (rank < activePoints[i].X)
				{
					rank = activePoints[i].X;
				}
				if (rank < activePoints[i].Y)
				{
					rank = activePoints[i].Y;
				}
			}
			if (rank == 0)
			{
				rank = 1;
			}
		}
	}
}
